package com.distributed.tcp.manager;

import com.distributed.tcp.config.TcpServerProperties;
import com.distributed.tcp.model.DeviceConnection;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.distributed.tcp.utils.IpUtils.getLocalHostAddress;

/**
 * 注册客户端连接，以及续约操作
 */
@Slf4j
@Component
public class DeviceConnectionManager {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private TcpServerProperties tcpProperties;

    @Resource
    private ObjectMapper objectMapper;

    @Value("${server.port}")
    private int port;

    @Value("${spring.application.name}")
    private String serverName;

    // 本地连接缓存
    private final Map<String, DeviceConnection> localConnections = new ConcurrentHashMap<>();
    public static final String CONNECTION_PREFIX = "tcp:connection:";

    /**
     * 注册新连接
     */
    public void registerConnection(Channel channel,String deviceCode) {
        //只有正确的设备信息存在，才能进行注册服务channel，其他不存在的连接，丢弃
        String ipAddress = getLocalHostAddress();
        DeviceConnection connection = new DeviceConnection(port, ipAddress,deviceCode,serverName);
        connection.setChannel(channel);
        localConnections.put(deviceCode, connection);

        try {
            // 存储连接信息到Redis
            redisTemplate.opsForValue().set(
                CONNECTION_PREFIX + deviceCode,
                    objectMapper.writeValueAsString(connection),
                tcpProperties.getConnectionTimeout(),
                TimeUnit.SECONDS
            );
            redisTemplate.expire(
                    CONNECTION_PREFIX + deviceCode,
                    tcpProperties.getConnectionTimeout(),
                    TimeUnit.SECONDS
            );

        } catch (Exception e) {
            log.error("序列化连接信息失败", e);
            localConnections.remove(deviceCode);
        }

        log.info("新连接注册: deviceCode={}, port={}, instance={}",
                deviceCode, port, ipAddress);

    }

    /**
     * 续约本地连接
     */
    public Boolean renewalDevice(String deviceCode) {
        String key = CONNECTION_PREFIX + deviceCode;
        // 判断key是否存在（可选，根据业务需求）
        if (Boolean.FALSE.equals(redisTemplate.hasKey(key))) {
            return false; // key已不存在，续约失败
        }
        // 延长过期时间（覆盖原TTL）
        return redisTemplate.expire(key, tcpProperties.getConnectionTimeout(), TimeUnit.SECONDS);
    }

    /**
     * 移除连接
     */
    public void removeConnection(String deviceCode) {
        DeviceConnection connection = localConnections.remove(deviceCode);
        if (connection == null) return;

        // 移除Redis中的连接信息
        redisTemplate.delete(CONNECTION_PREFIX + deviceCode);

        log.info("设备连接已移除: deviceId={}", connection.getDeviceCode());
    }

    /**
     * 根据设备ID获取连接信息（跨实例）
     */
    public DeviceConnection getConnectionByDeviceId(String deviceCode) {
        try {
            // 从Redis获取设备对应连接信息
            String connectionJson = (String) redisTemplate.opsForValue().get(CONNECTION_PREFIX + deviceCode);
            if (connectionJson == null) return null;

            return objectMapper.readValue(connectionJson, DeviceConnection.class);
        } catch (Exception e) {
            log.error("获取设备连接信息失败", e);
            return null;
        }
    }

    /**
     * 获取本地设备连接
     */
    public DeviceConnection getLocalDeviceConnection(String deviceCode) {
        return localConnections.values().stream()
                .filter(conn -> deviceCode.equals(conn.getDeviceCode()))
                .findFirst()
                .orElse(null);
    }

    /**
     * 检查连接是否为本地连接
     */
    public boolean isLocalConnection(DeviceConnection connection) {
        return connection != null &&
                getLocalHostAddress().equals(connection.getHost());
    }
}
